use crate::modeling::Value;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

///```json
///{
///    "env": {"",""},
///    "current":"",
///    "script": [["ls"],["cd /"]],
///}
///```
#[derive(Debug, Deserialize, Serialize)]
pub struct CliRequest {
    pub env: Option<HashMap<String, String>>,
    pub current: Option<String>,
    pub commands: Vec<Vec<String>>,
    pub timeout: Option<u32>,
    pub stdout: Option<bool>,
    pub stderr: Option<bool>,
}

///```json
///{
///    "env": {"",""},
///    "current":"",
///    "script": [["ls"],["cd /"]],
///}
///```
#[derive(Debug, Deserialize, Serialize)]
pub struct BatchScriptRequest {
    pub env: Option<HashMap<String, String>>,
    pub current: Option<String>,
    pub script: String,
    pub timeout: Option<u32>,
    pub stdout: Option<bool>,
    pub stderr: Option<bool>,
}

#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]

pub struct CommandResult {
    pub command: Vec<String>,
    pub code: i32,
    pub outputs: Option<String>,
    pub errors: Option<String>,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct CliResponse {
    pub results: Vec<CommandResult>,
    pub extensions: HashMap<String, String>,
}

impl CliRequest {
    pub fn command_noargs(cmd: String) -> anyhow::Result<Self> {
        Ok(Self {
            env: None,
            current: None,
            commands: vec![vec![cmd]],
            timeout: None,
            stdout: None,
            stderr: None,
        })
    }
    pub fn command(cmd: Vec<String>) -> anyhow::Result<Self> {
        Ok(Self {
            env: None,
            current: None,
            commands: vec![cmd],
            timeout: None,
            stdout: None,
            stderr: None,
        })
    }
    pub fn commands(cmds: Vec<Vec<String>>) -> anyhow::Result<Self> {
        Ok(Self {
            env: None,
            current: None,
            commands: cmds,
            timeout: None,
            stdout: None,
            stderr: None,
        })
    }
}

impl BatchScriptRequest {
    pub fn new(cmd: String) -> anyhow::Result<Self> {
        Ok(Self {
            env: None,
            current: None,
            script: cmd,
            timeout: None,
            stdout: None,
            stderr: None,
        })
    }
}

impl TryFrom<Value> for CliRequest {
    type Error = anyhow::Error;

    fn try_from(value: Value) -> Result<Self, Self::Error> {
        let serde_value: serde_json::Value = value.try_into()?;
        let message = serde_json::from_value(serde_value)?;
        Ok(message)
    }
}

impl TryInto<Value> for CliRequest {
    type Error = anyhow::Error;

    fn try_into(self) -> Result<Value, Self::Error> {
        Value::try_from(serde_json::json!(self))
    }
}
impl TryFrom<Value> for BatchScriptRequest {
    type Error = anyhow::Error;

    fn try_from(value: Value) -> Result<Self, Self::Error> {
        let serde_value: serde_json::Value = value.try_into()?;
        let message = serde_json::from_value(serde_value)?;
        Ok(message)
    }
}

impl TryInto<Value> for BatchScriptRequest {
    type Error = anyhow::Error;

    fn try_into(self) -> Result<Value, Self::Error> {
        Value::try_from(serde_json::json!(self))
    }
}

impl TryFrom<Value> for CliResponse {
    type Error = anyhow::Error;

    fn try_from(value: Value) -> Result<Self, Self::Error> {
        let serde_value: serde_json::Value = value.try_into()?;
        let message = serde_json::from_value(serde_value)?;
        Ok(message)
    }
}

impl TryInto<Value> for CliResponse {
    type Error = anyhow::Error;

    fn try_into(self) -> Result<Value, Self::Error> {
        Value::try_from(serde_json::json!(self))
    }
}

impl TryInto<Value> for CommandResult {
    type Error = anyhow::Error;

    fn try_into(self) -> Result<Value, Self::Error> {
        Value::try_from(serde_json::json!(self))
    }
}

impl CommandResult {
    pub fn to_value_map(self) -> anyhow::Result<HashMap<String, Value>> {
        let v: Value = self.try_into()?;

        match v {
            Value::Object(m) => Ok(m),
            _ => Err(anyhow::anyhow!("转换失败:{:?}", v)),
        }
    }
}
